﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media.Media3D;
using System.Windows.Input;
using Eniux.Windows.ThreeDEngine.AttachObject;

namespace Eniux.Windows.ThreeDEngine.Base.CameraControllers
{
    /// <summary>
    /// 缩放操作
    /// </summary>
    internal class ZoomHandler : MouseGestureHandler
    {
        #region 常量、字段

        /// <summary>
        ///   改变观察角度
        /// </summary>
        private readonly bool changeFieldOfView;

        /// <summary>
        ///  缩放2D点
        /// </summary>
        private Point zoomPoint;

        /// <summary>
        ///  缩放3D点
        /// </summary>
        private Point3D zoomPoint3D;

        #endregion

        #region 构造、析构

        /// <summary>
        /// 
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="changeFieldOfView"></param>
        public ZoomHandler(CameraController controller, bool changeFieldOfView = false)
            : base(controller)
        {
            this.changeFieldOfView = changeFieldOfView;
        }

        #endregion

        #region 公共方法

        /// <summary>
        /// 改变相机位置
        /// </summary>
        /// <param name="delta"></param>
        /// <param name="zoomAround"></param>
        public void ChangeCameraPosition(double delta, Point3D zoomAround)
        {
            if (!AttachCamera.GetIsZoomEnabled(Controller))
            {
                return;
            }

            if (delta < -0.5)
            {
                delta = -0.5;
            }

            delta *= this.ZoomSensitivity;
            switch (this.CameraMode)
            {
                case CameraMode.Inspect:
                    Point3D target = this.CameraPosition + this.CameraLookDirection;
                    Vector3D relativeTarget = zoomAround - target;
                    Vector3D relativePosition = zoomAround - this.CameraPosition;

                    Vector3D newRelativePosition = relativePosition * (1 + delta);
                    Vector3D newRelativeTarget = relativeTarget * (1 + delta);

                    Point3D newTarget = zoomAround - newRelativeTarget;
                    Point3D newPosition = zoomAround - newRelativePosition;
                    Vector3D newLookDirection = newTarget - newPosition;

                    this.CameraLookDirection = newLookDirection;
                    this.CameraPosition = newPosition;
                    break;
                case CameraMode.WalkAround:
                    this.CameraPosition -= this.CameraLookDirection * delta;
                    break;
            }
        }

        /// <summary>
        /// 改变相机宽度
        /// </summary>
        /// <param name="delta"></param>
        /// <param name="zoomAround"></param>
        public void ChangeCameraWidth(double delta, Point3D zoomAround)
        {
            switch (this.CameraMode)
            {
                case CameraMode.WalkAround:
                case CameraMode.Inspect:
                case CameraMode.FixedPosition:
                    var ocamera = this.Camera as OrthographicCamera;
                    if (ocamera != null)
                    {
                        ocamera.Width *= 1 + delta;
                    }

                    break;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="delta"></param>
        public void ChangeFieldOfView(double delta)
        {
            var pcamera = this.Camera as PerspectiveCamera;
            if (pcamera == null)
            {
                return;
            }

            if (!this.Controller.IsChangeFieldOfViewEnabled)
            {
                return;
            }

            double fov = pcamera.FieldOfView;
            double d = this.CameraLookDirection.Length;
            double r = d * Math.Tan(0.5 * fov / 180 * Math.PI);

            fov *= 1 + delta * 0.5;
            if (fov < AttachCamera.GetMinimumFieldOfView(Controller))
            {
                fov = AttachCamera.GetMinimumFieldOfView(Controller);
            }

            if (fov > AttachCamera.GetMaximumFieldOfView(Controller))
            {
                fov = AttachCamera.GetMaximumFieldOfView(Controller);
            }

            pcamera.FieldOfView = fov;
            double d2 = r / Math.Tan(0.5 * fov / 180 * Math.PI);
            Vector3D newLookDirection = this.CameraLookDirection;
            newLookDirection.Normalize();
            newLookDirection *= d2;
            Point3D target = this.CameraPosition + this.CameraLookDirection;
            this.CameraPosition = target - newLookDirection;
            this.CameraLookDirection = newLookDirection;
        }

        /// <summary>
        /// 操作完成时，引发该事件
        /// </summary>
        /// <param name="e"></param>
        public override void Completed(ManipulationEventArgs e)
        {
            base.Completed(e);
            this.Controller.HideTargetAdorner();
        }

        /// <summary>
        /// 触摸操作时，位置点发生改变，引发该事件
        /// </summary>
        /// <param name="e"></param>
        public override void Delta(ManipulationEventArgs e)
        {
            var delta = e.CurrentPosition - this.LastPoint;
            this.LastPoint = e.CurrentPosition;
            this.Zoom(delta.Y * 0.01, this.zoomPoint3D);
        }

        /// <summary>
        /// 当操作开始时，引发该事件
        /// </summary>
        /// <param name="e"></param>
        public override void Started(ManipulationEventArgs e)
        {
            base.Started(e);
            this.zoomPoint = new Point(
                this.Controller.Viewport.ActualWidth / 2, this.Controller.Viewport.ActualHeight / 2);
            this.zoomPoint3D = this.Controller.CameraTarget;

            if (AttachCamera.GetZoomAroundMouseDownPoint(Controller) && this.MouseDownNearestPoint3D != null)
            {
                this.zoomPoint = this.MouseDownPoint;
                this.zoomPoint3D = this.MouseDownNearestPoint3D.Value;
            }

            if (!this.changeFieldOfView)
            {
                this.Controller.ShowTargetAdorner(this.zoomPoint);
            }
        }

        /// <summary>
        /// 缩放
        /// </summary>
        /// <param name="delta"></param>
        public void Zoom(double delta)
        {
            this.Zoom(delta, this.CameraTarget);
        }

        /// <summary>
        /// 围绕指定点缩放
        /// </summary>
        /// <param name="delta"></param>
        /// <param name="zoomAround"></param>
        public void Zoom(double delta, Point3D zoomAround)
        {
            if (this.Camera is PerspectiveCamera)
            {
                if (this.CameraMode == CameraMode.FixedPosition || this.changeFieldOfView)
                {
                    this.ChangeFieldOfView(delta);
                }
                else
                {
                    this.ChangeCameraPosition(delta, zoomAround);
                }
            }

            if (this.Camera is OrthographicCamera)
            {
                this.ChangeCameraWidth(delta, zoomAround);
            }
        }

        #endregion

        #region 方法

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected override bool CanExecute()
        {
            if (this.changeFieldOfView)
            {
                return this.Controller.IsChangeFieldOfViewEnabled && this.Controller.ActualCamera is PerspectiveCamera;
            }

            return AttachCamera.GetIsZoomEnabled(Controller);
        }

        /// <summary>
        /// 获取缩放光标
        /// </summary>
        /// <returns></returns>
        protected override Cursor GetCursor()
        {
            return AttachCamera.GetZoomCursor(Controller);
        }

        #endregion
    }
}
